feat(mvux): advanced mocking & previews — spec 013 + implementation (WIP) - #3165
Conversation
Restored state = end of discussion (lost commits 8d589d9 -> 292fb5f ->
2618def -> cd4c9ad), merged from local recovery notes + David's VS Code
copies (his architecture.md was the most recent, post-2618def).
- spec.md: 3 tiers, real VM + real Model with null-injected services,
derived feeds survive via Model-feed cache swap anchor, external
mocking generator (consumer test project), D1-D9 decision log,
open question: context-wide scope / ambient MockingService.Enable()
- architecture.md: seams, swap anchor, MVUX gen (analysis + dependency
attributes + hidden typed hooks) vs Mocking gen (metadata-driven
{Model}Mock/Create/SetModel), tier-1 authorable plain-CLR MessageEntry
(not a DependencyObject, not observable) with first-class custom axes,
natural feed evolution contract, XAML examples
- implementation.md: package split, attribute shapes, hidden hooks,
typed vocabulary, diagnostics FEED3201-3203/MOCK0001, P0 canaries
(derived-swap gate, null-inject, identity matrix, context spike),
test plan, docs plan
- history.md: full version/decision chronology of the spec discussion
- spec.md: audience/deliverable split, derived-feeds-survive flow, three-tier layering, generator responsibility split, end-to-end sequence, tier-1 evolution sequence; risks and decision log as tables; status header cleaned for team/CEO review (history stays in history.md) - architecture.md: swap-anchor mermaid in section 1, end-to-end ASCII flow replaced by a sequence diagram in section 5
…he swap fail hard Records the decision to drop the dedicated static flag in favour of a per-context bit, and to reuse the hot-reload reflection driver with strict failure instead of best effort.
Adds a per-context bit inherited at creation and reads it where the wrap is decided, so only contexts created under an activation scope wrap their feeds and a live application pays nothing.
Classifies every feed member as service dependent, derived or independent, and instruments the constructors so an eagerly dereferenced service is reported. The metadata is what the consumer generator reads.
Commands have no hot-swap state, so a dedicated seam reassigns the command property after construction. Construction itself needs no seam: the public constructors plus the ambient scope are enough.
Adds the activation scope, the swap engine and the typed feed vocabulary used by the generated mocks.
The consumer generator reads the app metadata and emits the mock record, the Create factories and the SetModel facade. StateImpl always implements IHotSwapState, so the previous type test never rejected a non-wrapped feed and the swap silently did nothing. Added CanHotSwap and made the engine throw when a mocked feed is not swappable.
Adds Value, Empty, Undefined, Loading, Error and Refreshing for both scalar feeds and list feeds, built on pinned messages and strongly typed end to end.
…cabulary The generated record exposes Empty with every input pinned to its empty state, Create() builds from it, and commands can be overridden through the view-model seam.
Logs what landed for the tier 2 and 3 implementation and what is left out of scope.
The mocked feed vocabulary already exists in Uno.HotTesting.Reactive, so the duplicate assembly is removed and everything lives there: FeedMock and ListFeedMock are reused, CommandMock is added, and the swap engine moves into MockingService as strongly typed helpers. The consumer generator becomes Uno.HotTesting.Reactive.Generator and emits raw string literals. The MVUX instrumentation is now emitted by default, with an assembly level opt-out.
…ourceContext The ambient activation state is a mocking concern, not a Core one. MockingService now owns the AsyncLocal and registers a probe; SourceContext only keeps the per-instance IsMockingActive bit. In a live application the probe is never registered, so the bit stays false and the cost is zero.
ff6f0d5 to
7893286
Compare
Extended the reactive testing reference with the generated tier 2 and 3 layer, and added a
named catalog sample together with its test. The generated factory class is now named
{Vm}Mock and Empty moved onto the record.
… Create
Review feedback: the generated {Vm}Mock is now partial so applications can extend it with
named catalogs, SetModel is renamed to SetMock, Create only takes the mock record,
{Model}Mock.Empty stays on the record so it composes with `with`, and Create opens the
MockingService.Enable() scope itself so user code never has to.
Command mocking is deferred to a future version: the consumer generator no longer emits a
command member nor the __Mock_SetCommand wiring.
Applied: combined the nested if in ViewModelGenTool_3.Mocking, projected ctor.DeclaringSyntaxReferences, SimpleNameSyntax and the accesses with Select, and filtered the nested types and the references explicitly in FeedsMockGenerator. Declined, each answered in its thread: the readonly suggestion on Disposable._onDispose is a false positive (the field is a ref argument of Interlocked.Exchange), the == false simplification targets a bool? whose three-state semantics are intentional, and the Where suggestion filters a computed value.
5191d49 to
18f0bf2
Compare
|
Code-quality review addressed in 18f0bf2. Applied (7): nested- Declined (3), each answered in its thread: the Tests: Given_MockingActivation 4/4, Given_MockingRuntime 4/4, Given_GeneratedMock 4/4, Tests.Generator 80/80, Uno.HotTesting.Reactive.Tests 22/22. |
The repository enables GenerateDocumentationFile together with TreatWarningsAsErrors, so a public member without an XML comment fails the Release build even though a local Debug build stays green. Added /// <inheritdoc /> on FeedsMockGenerator.Initialize and Execute, the same way FeedsGenerator already does. Also fixed MD032, MD012, MD022 and MD031 on the four spec 013 documents.
Everything pushed to GitHub must be in English. The spec 013 history document was written in French during the design discussions, which failed the CI spell-checking validation (333 unknown words). spec.md, architecture.md and the reference doc were already clean. - history.md fully translated to English, keeping every SHA, type name, file path and decision id unchanged. - implementation.md: replaced three coined words the dictionary rejects (bikesheddable, Authorable, mockability) with plain English equivalents. Verified with the exact CI commands: cspell over 236 markdown files reports 0 issue, and markdownlint reports no violation.
18f0bf2 to
de78a92
Compare
|
CI failures fixed (head is now de78a92). Four independent causes:
Both linters were re-run with the exact CI commands over the whole repository: cspell reports 0 issue across 236 markdown files, and markdownlint reports no violation. |
4e897d3 to
4a3e1b3
Compare
|
Tick the box to add this pull request to the merge queue (same as
|
|
@Mergifyio backport servicing/7.4 |
✅ Backports have been createdDetails
|
Summary
Draft PR tracking the "Advanced MVUX mocking & previews" work.
Lands Spec 013 – MVUX Mocking & Previews (design under
specs/013-mvux-mocking-previews/) and will carry the implementation on top.Kept as draft until the implementation is in.
Scope
MessageEntryCLR type in CoreRelated
Closes #3150
Depends on #3149 (per issue, should land first)